package com.uinnova.product.eam.init.cmdb;

import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.constant.ArchReviewState;
import com.uinnova.product.eam.dao.CmdbSysSyncInfoDao;
import com.uinnova.product.eam.dao.CmdbSysSyncLogDao;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.es.CmdbSysSyncInfo;
import com.uinnova.product.eam.model.es.CmdbSysSyncLog;
import com.uinnova.product.eam.service.ArchReviewService;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.cmdb.flowable.CMDBFlowableInfo;
import com.uinnova.product.eam.service.cmdb.flowable.CMDBFlowableSvc;
import com.uinnova.product.eam.service.cmdb.flowable.CmdbFlowResponse;
import com.uinnova.product.eam.service.cmdb.flowable.FromValue;
import com.uinnova.product.eam.vo.ArchReviewQueryReq;
import com.uinnova.product.eam.vo.ArchReviewRes;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.dao.util.ESUtil;
import com.uino.service.cmdb.microservice.impl.CIClassSvc;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SyncCmdbSysSvc {

    @Resource
    private CIClassSvc ciClassSvc;
    @Resource
    private ICISwitchSvc iciSwitchSvc;
    @Resource
    private ArchReviewService archReviewService;
    @Resource
    private PlanDesignInstanceService planDesignInstanceService;
    @Resource
    private CMDBFlowableSvc cmdbFlowableSvc;
    @Resource
    private CmdbSysSyncInfoDao cmdbSysSyncInfoDao;
    @Resource
    private CmdbSysSyncLogDao cmdbSysSyncLogDao;

    private boolean isSyncing = false;

    public void syncCmdbSys(JSONObject jsonConfig) {
        if (isSyncing) {
            log.info("已有任务正在同步！");
            return;
        }
        isSyncing = true;
        try {
            //配置处理
            String systemClassCode = jsonConfig.getString("systemClassCode");
            if (StringUtils.isBlank(systemClassCode)) {
                log.error("无法获取视图配置项：systemClassCode");
                throw new BinaryException("无法获取视图配置项：systemClassCode");
            }
            JSONObject systemMap = jsonConfig.getJSONObject("systemMap");
            checkSysMap(systemMap);

            // 查询系统分类
            List<CcCiClassInfo> ciClassInfos = getClassInfos(Collections.singletonList(systemClassCode));
            if (CollectionUtils.isEmpty(ciClassInfos)) {
                log.error("系统分类未查到：{}", systemClassCode);
                throw new BinaryException("系统分类未查到");
            }
            Long systemClassId = ciClassInfos.get(0).getCiClass().getId();

            // 当前轮询时间
            Long currentTime = ESUtil.getNumberDateTime();
            log.info("CMDB新建系统流程调用任务开始执行，执行时间：{}", currentTime);
            /**
             * 查询待推送系统数据
             * 1、架构方案评审流程通过
             * 2、关联架构方案“关联资产”或“涉及变动系统”，内资产实例，为架构管理平台新建系统（“CMDB标识ID”或者“系统编号”为空）
             * 3、该新建系统未提交CMDB新建系统流程（系统未调用接口）
             */

            // 查询上次轮询时间 用来作为起始时间查询后续评审通过的方案 避免同一流程重复查询
            Long lastTime = null;
            // 已经推送过的ciCode集合
            List<String> pushedCiCodes = new ArrayList<>();
            Page<CmdbSysSyncInfo> lastSyncInfos = cmdbSysSyncInfoDao.getSortListByQuery(1, 1, QueryBuilders.boolQuery(), "createTime", false);
            if (!CollectionUtils.isEmpty(lastSyncInfos.getData())) {
                lastTime = lastSyncInfos.getData().get(0).getBatchTime();
                Page<CmdbSysSyncInfo> allInfos = cmdbSysSyncInfoDao.getSortListByQuery(1, (int) lastSyncInfos.getTotalRows(), QueryBuilders.boolQuery(), "createTime", false);
                pushedCiCodes = allInfos.getData().stream().map(CmdbSysSyncInfo::getCiCode).collect(Collectors.toList());
            }

            //查询符合条件的评审流程
            List<ArchReviewRes> archReviewResList = getArchReviewResList(lastTime);
            if (archReviewResList == null) return;

            Set<String> ciCodeSet = new HashSet<>();
            Map<String, CmdbSysSyncInfo> ciReviewMap = new HashMap<>();
            for (ArchReviewRes archReviewRes : archReviewResList) {
                CmdbSysSyncInfo cmdbSysSyncInfo = new CmdbSysSyncInfo();
                cmdbSysSyncInfo.setProcessInstanceId(archReviewRes.getProcessInstanceId());
                cmdbSysSyncInfo.setReviewId(archReviewRes.getId());
                cmdbSysSyncInfo.setItemName(archReviewRes.getItemName());
                cmdbSysSyncInfo.setProCreateUserCode(archReviewRes.getCreatorCode());
                List<PlanDesignInstance> plans = planDesignInstanceService.getByIds(archReviewRes.getPlanIds());
                if (CollectionUtils.isEmpty(plans)) {
                    log.info("流程：{} 下方案未查询到！", archReviewRes.getProcessInstanceId());
                    continue;
                }
                for (PlanDesignInstance planInstance : plans) {
                    if (systemClassId.equals(planInstance.getDefaultSystemClassId()) && !pushedCiCodes.contains(planInstance.getDefaultSystemCiCode())) {
                        ciCodeSet.add(planInstance.getDefaultSystemCiCode());
                        ciReviewMap.put(planInstance.getDefaultSystemCiCode(), cmdbSysSyncInfo);
                    }
                    if (!CollectionUtils.isEmpty(planInstance.getSystemCiCodes())) {
                        for (String code : planInstance.getSystemCiCodes()) {
                            if (!pushedCiCodes.contains(code)) {
                                ciCodeSet.add(code);
                                ciReviewMap.put(code, cmdbSysSyncInfo);
                            }

                        }
                    }
                }
            }
            if (CollectionUtils.isEmpty(ciCodeSet)) {
                log.info("待同步数据ciCodes为空");
                return;
            }

            // 根据ciCodes 查询相关ci
            CCcCi cdt = new CCcCi();
            cdt.setClassId(systemClassId);
            cdt.setCiCodes(ciCodeSet.toArray(new String[0]));
            List<CcCiInfo> ciInfos = iciSwitchSvc.queryCiInfoList(1l, cdt, null, false, false, LibType.DESIGN);

            if (CollectionUtils.isEmpty(ciInfos)) {
                log.info("待同步数据ciInfos为空");
                return;
            }
            // 遍历ci数据 组织待推送数据 将数据存入推送池
            List<CmdbSysSyncInfo> cmdbSysSyncInfos = new ArrayList<>();
            for (CcCiInfo ccCiInfo : ciInfos) {
                CmdbSysSyncInfo cmdbSysSyncInfo = new CmdbSysSyncInfo();
                String ciCode = ccCiInfo.getCi().getCiCode();
                Map<String, String> ciAttrs = ccCiInfo.getAttrs();
                cmdbSysSyncInfo.setCiCode(ciCode);

                fillSyncInfo(systemMap, cmdbSysSyncInfo, ciAttrs);

                // 如果系统编号/cmdbId 都不为空 则无需推送
                if (!BinaryUtils.isEmpty(cmdbSysSyncInfo.getSystemNum()) && !BinaryUtils.isEmpty(cmdbSysSyncInfo.getCmdbId())) {
                    continue;
                }

                cmdbSysSyncInfo.setProcessInstanceId(ciReviewMap.get(ciCode).getProcessInstanceId());
                cmdbSysSyncInfo.setReviewId(ciReviewMap.get(ciCode).getReviewId());
                cmdbSysSyncInfo.setItemName(ciReviewMap.get(ciCode).getItemName());
                cmdbSysSyncInfo.setProCreateUserCode(ciReviewMap.get(ciCode).getProCreateUserCode());

                cmdbSysSyncInfo.setLastPushStatus("未推送");
                cmdbSysSyncInfo.setBatchTime(currentTime);

                cmdbSysSyncInfos.add(cmdbSysSyncInfo);
            }
            cmdbSysSyncInfoDao.saveOrUpdateBatch(cmdbSysSyncInfos);

            // 查询待推送数据 遍历推送数据
            BoolQueryBuilder query = QueryBuilders.boolQuery();
            query.must(QueryBuilders.termQuery("lastPushStatus.keyword", "未推送"));
            Page<CmdbSysSyncInfo> infoPage = cmdbSysSyncInfoDao.getListByQuery(1, 3000, query);
            if (CollectionUtils.isEmpty(infoPage.getData())) {
                log.info("待推送数据 infoPage.getData 为空");
                return;
            }
            // 推送记录
            List<CmdbSysSyncLog> cmdbSysSyncLogs = new ArrayList<>();
            for (CmdbSysSyncInfo info : infoPage.getData()) {

                CmdbFlowResponse response = pushInfo(info);

                // 推送记录
                Long pushTime = ESUtil.getNumberDateTime();
                CmdbSysSyncLog cmdbSysSyncLog = new CmdbSysSyncLog();
                cmdbSysSyncLog.setCiCode(info.getCiCode());
                cmdbSysSyncLog.setUserCode("system");
                cmdbSysSyncLog.setUserName("系统用户");
                cmdbSysSyncLog.setPushTime(pushTime);
                info.setLastPushTime(pushTime);
                if (response.getSuccess()) {
                    cmdbSysSyncLog.setPushStatus("已推送");
                    cmdbSysSyncLog.setPushResult("推送成功");
                    info.setLastPushStatus("已推送");
                    info.setLastPushResult("推送成功");
                } else {
                    cmdbSysSyncLog.setPushStatus("推送失败");
                    cmdbSysSyncLog.setPushResult(response.getError());
                    info.setLastPushStatus("推送失败");
                    info.setLastPushResult(response.getError());
                }
                cmdbSysSyncLogs.add(cmdbSysSyncLog);

                // 更新主推送信息的推送状态

                cmdbSysSyncInfoDao.saveOrUpdate(info);
            }
            // 保存推送记录
            cmdbSysSyncLogDao.saveOrUpdateBatch(cmdbSysSyncLogs);

        } catch (Exception exception) {
            log.error("syncCmdbSys 异常！" + exception.getMessage());
        } finally {
            isSyncing = false;
        }
    }

    /**
     * 丰富系统相关属性
     * @param systemMap
     * @param cmdbSysSyncInfo
     * @param ciAttrs
     */
    public void fillSyncInfo(JSONObject systemMap, CmdbSysSyncInfo cmdbSysSyncInfo, Map<String, String> ciAttrs) {
        cmdbSysSyncInfo.setNum(dealStrValue(ciAttrs.get(systemMap.getString("num"))));
        cmdbSysSyncInfo.setSystemNum(dealStrValue(ciAttrs.get(systemMap.getString("systemNum"))));
        cmdbSysSyncInfo.setSystemName(dealStrValue(ciAttrs.get(systemMap.getString("systemName"))));
        cmdbSysSyncInfo.setSystemShortName(dealStrValue(ciAttrs.get(systemMap.getString("systemShortName"))));
        cmdbSysSyncInfo.setCmdbId(dealStrValue(ciAttrs.get(systemMap.getString("cmdbId"))));
        cmdbSysSyncInfo.setSystemStatus(dealStrValue(ciAttrs.get(systemMap.getString("systemStatus"))));
        cmdbSysSyncInfo.setIsChild(dealStrValue(ciAttrs.get(systemMap.getString("isChild"))));
        cmdbSysSyncInfo.setBuildYear(dealStrValue(ciAttrs.get(systemMap.getString("buildYear"))));
        cmdbSysSyncInfo.setDescribe(dealStrValue(ciAttrs.get(systemMap.getString("describe"))));
    }

    /**
     * systemMap检查
     * @param systemMap
     */
    public void checkSysMap(JSONObject systemMap) {
        if (BinaryUtils.isEmpty(systemMap)) {
            log.error("无法获取视图配置项：systemMap");
            throw new BinaryException("无法获取视图配置项：systemMap");
        }
        BinaryUtils.checkEmpty(systemMap.getString("num"), "num");
        BinaryUtils.checkEmpty(systemMap.getString("systemNum"), "systemNum");
        BinaryUtils.checkEmpty(systemMap.getString("systemName"), "systemName");
        BinaryUtils.checkEmpty(systemMap.getString("systemShortName"), "systemShortName");
        BinaryUtils.checkEmpty(systemMap.getString("cmdbId"), "cmdbId");
        BinaryUtils.checkEmpty(systemMap.getString("systemStatus"), "systemStatus");
        BinaryUtils.checkEmpty(systemMap.getString("isChild"), "isChild");
        BinaryUtils.checkEmpty(systemMap.getString("buildYear"), "buildYear");
        BinaryUtils.checkEmpty(systemMap.getString("describe"), "describe");
    }

    /**
     * 推送数据
     * @param info
     * @return
     */
    public CmdbFlowResponse pushInfo(CmdbSysSyncInfo info) {
        CmdbFlowResponse response = new CmdbFlowResponse();
        try {
            // 组织推送数据入参
            CMDBFlowableInfo cmdbFlowableInfo = new CMDBFlowableInfo();
            cmdbFlowableInfo.setName(info.getItemName());
            // 提交人
            cmdbFlowableInfo.setAssigneeUsers(Collections.singletonList(info.getProCreateUserCode()));

            FromValue fromValue = new FromValue();
            fromValue.setName(info.getSystemName());
            fromValue.setShortName(info.getSystemShortName());
            fromValue.setSysStatus(info.getSystemStatus());
            fromValue.set_category(info.getIsChild());
            fromValue.setBuildYear(info.getBuildYear());
            fromValue.setDescribe(info.getDescribe());
            fromValue.setEamId(info.getCiCode());
            List<FromValue> fromValues = new ArrayList<>();
            fromValues.add(fromValue);
            cmdbFlowableInfo.setFromValue(fromValues);
            log.info("推送数据 info：" + info);
            log.info("推送数据 cmdbFlowableInfo ：" + cmdbFlowableInfo);
            // 推送
            response = cmdbFlowableSvc.sendCMDBFlowable(cmdbFlowableInfo);
        } catch (Exception exception) {
            log.info("ciCod: {} 推送异常：{}", info.getCiCode(), exception.getMessage());
            response.setSuccess(false);
            response.setError("推送异常：" + exception.getMessage());
        }
        return response;
    }

    /**
     * 已完成评审查询
     * @return
     */
    private List<ArchReviewRes> getArchReviewResList(Long lastTime) {
        List<ArchReviewRes> archReviewResList = new ArrayList<>();
        ArchReviewQueryReq req = new ArchReviewQueryReq();
        req.setPageNum(1);
        req.setPageSize(100);
        req.setStates(Collections.singletonList(ArchReviewState.PASS));
        if (!BinaryUtils.isEmpty(lastTime)) {
            req.setProcessFinishTimeBegin(lastTime);
        }
        Page<ArchReviewRes> archReviewResPage = archReviewService.page(req);
        if (CollectionUtils.isEmpty(archReviewResPage.getData())) {
            log.info("没有符合条件的评审流程，退出！");
            return null;
        }
        archReviewResList.addAll(archReviewResPage.getData());
        for (int i = 2; i <= archReviewResPage.getTotalPages(); i++ ) {
            req.setPageNum(i);
            archReviewResPage = archReviewService.page(req);
            if (!CollectionUtils.isEmpty(archReviewResPage.getData())) {
                archReviewResList.addAll(archReviewResPage.getData());
            }
        }
        return archReviewResList;
    }

    /**
     * 分类查询
     * @param classCodes
     * @return
     */
    public List<CcCiClassInfo> getClassInfos(List<String> classCodes) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodes(classCodes.toArray(new String[0]));
        return ciClassSvc.queryClassByCdt(cdt);
    }

    /**
     * 属性值处理
     * @param attrValue
     * @return
     */
    private String dealStrValue(Object attrValue) {
        if (BinaryUtils.isEmpty(attrValue)) {
            return "";
        }
        String result = String.valueOf(attrValue);
        if (result == null || "null".equals(result)) {
            result = "";
        }
        return result;
    }
}
